import fs from 'node:fs';
import FormData from 'form-data';
import COS from 'cos-nodejs-sdk-v5';
import OSS from 'ali-oss';

const UPLOAD_FILE_PATH = 'upload/shotstack/file'

export class Uploader {
  constructor(opt) {
    this.requestFactory = opt.requestFactory;
  }

  async getWorkAiConfig(){
    const { data: config } = await this.requestFactory.request({
      url: '/work_ai/config'
    })

    return config.list
  }

  async getUploadDriver(){
    let aiConfig = await this.getWorkAiConfig()

    if (aiConfig.shotstack_driver == "alionline"){
      aiConfig.upload_driver = 'oss'
    }
  
    return aiConfig.upload_driver
  }

  getCosConfig(scopes) {
    return this.requestFactory.request({
      url: '/work_ai/upload/cos/config',
      method: 'POST',
      data: {
        ...(scopes? { scopes } : {})
      }
    })
  }

  getOssConfig(){
    return this.requestFactory.request({
      url: '/work_ai/upload/oss/config/ststoken',
      method: 'POST',
      data: {
        is_pack: 1,
      }
    })
  }

  createLocalInstance(){
    const upload = async (filePath) => {
      const form = new FormData();
      form.append('file', fs.createReadStream(filePath), filePath.split('/').pop());
      form.append('dir', 'work_ai')

      const response = await this.requestFactory.request({
        url: '/public/system/uploadFile',
        method: 'POST',
        data: form,
        headers: {
         ...form.getHeaders()
        },
        onUploadProgress: (progressEvent) => {
          const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
          console.log(`上传进度 ${percentCompleted}%`);
        },
      })

      return response.data
    }
  
    return {
      upload: upload,
      multipartUpload: upload
    }
  }

  createCosInstance(){
    const cos = new COS({
      getAuthorization: async (options, callback) => {
        const { data: uploadConfig } = await this.getCosConfig(options.Scope)
  
        const credentials = uploadConfig.credentials;
  
        if(! credentials){
            throw new Error('获取临时凭证失败')
        }
  
        callback({
            TmpSecretId: credentials.tmpSecretId,
            TmpSecretKey: credentials.tmpSecretKey,
            SecurityToken: credentials.sessionToken,
            StartTime: uploadConfig.startTime,
            ExpiredTime: uploadConfig.expiredTime,
            ScopeLimit: true,
        });
      }
    });
  
    const upload = async (filePath, uploadPath, headers = {}) => {
      const { data: uploadConfig } = await this.requestFactory.request({
        url: '/work_ai/upload/cos/config',
        method: 'POST',
      })
  
      if (! uploadPath){
        const fileName = filePath.split('/').pop();
        uploadPath = `${UPLOAD_FILE_PATH}/${fileName}`
      }
  
      const responseData = await (() => {
        return new Promise((resolve, reject) => {
          cos.uploadFile({
            Bucket: uploadConfig.cos.bucket,
            Region: uploadConfig.cos.region,
            Key: uploadPath,
            FilePath: filePath,
            SliceSize: 1024 * 1024 * 5,
            AsyncLimit: 3,
            onProgress: (progressData) => {
              console.log(`上传进度: ${(progressData.percent * 100).toFixed(2) + '%'}`)
            },
            Headers: {
              ...headers
            }
          }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve({
                fileName: uploadPath,
                fileUrl: uploadConfig.cos.domain + '/' + uploadPath
              })
            }
          })
        })
      })()
  
      return responseData
    }
  
    return {
      client: cos,
      upload: upload,
      multipartUpload: upload,
      head: async function(filePath){
        cos.headObject({
          Bucket: uploadConfig.cos.bucket,
          Region: uploadConfig.cos.region,
          Key: filePath,
        }, function(err, data) {
            console.log('腾讯云上传的错误', err || data);
        });
      }
    }
  }

  async createOssInstance() {
    const response = await this.getOssConfig()
  
    const oss_config = response.data.sts;
  
    const client = new OSS({
      bucket: oss_config.bucket,
      region: oss_config.region,
      endpoint: oss_config.endpoint,
      accessKeyId: oss_config.AccessKeyId,
      accessKeySecret: oss_config.AccessKeySecret,
      secure: false,
      connectTimeout: 300000,
      timeout: 60000 * 60,
      stsToken: oss_config.stsToken,
    });
  
    return {
      client,
      upload: async (filePath, uploadPath, headers = {}) => {
        if (! uploadPath){
          const fileName = filePath.split('/').pop();
          uploadPath = `${UPLOAD_FILE_PATH}/${fileName}`
        }
  
        const responseData = await (() => {
          return new Promise(async (resolve, reject) => {
            try {
              const responseData = await client.put(
                uploadPath,
                filePath,
                {
                  headers: {
                    ...headers,
                  },
                }
              );
  
              resolve({
                fileName: responseData.name,
                fileUrl: `${oss_config.domain}/${responseData.name}`,
              })
            } catch (error) {
              console.log("阿里云上传的错误", error);
              reject(error);
            }
          })
        })()
  
        return responseData
      },
      multipartUpload: async (filePath, uploadPath, headers = {}) => {
        if (! uploadPath){
          const fileName = filePath.split('/').pop();
          uploadPath = `${UPLOAD_FILE_PATH}/${fileName}`
        }
  
        const responseData = await (() => {
          return new Promise(async (resolve, reject) => {
            try {
              const responseData = await client.multipartUpload(
                uploadPath,
                filePath,
                {
                  // partSize: config.partSize || 1024 * 1024 * 5,
                  // parallel: config.parallel || 2,
                  progress: (p) => {
                    console.log(`上传进度: ${(p * 100).toFixed(2) + "%"}`);
                  },
                  headers: {
                    ...headers,
                  },
                }
              );
  
              resolve({
                fileName: responseData.name,
                fileUrl: `${oss_config.domain}/${responseData.name}`,
              })
            } catch (error) {
              console.log('阿里云上传的错误', error);
              reject(error);
            }
          })
        })()
  
        return responseData
      },
      head: async function(filePath){
        return client.head(filePath)
      }
    }
  }
  
  createUploader(driver) {
    if(driver === 'cos'){
      return this.createCosInstance()
    }
  
    if(driver === 'oss'){
      return this.createOssInstance()
    }
  
    return this.createLocalInstance()
  }
}

export function UploaderFactory(opt) {
  const uploader = opt.uploader;
  
  return {
    upload: async function(filePath, uploadPath){
      const uploadDriver = await uploader.getUploadDriver()
      const driver = await uploader.createUploader(uploadDriver)
      return driver.upload(filePath.replaceAll('\\', '/'),  uploadPath)
    },
    multipartUpload: async function(filePath, uploadPath){
      const uploadDriver = await uploader.getUploadDriver()
      const driver = await uploader.createUploader(uploadDriver)
      return driver.multipartUpload(filePath.replaceAll('\\', '/'),  uploadPath)
    },
    getUploadDriver: uploader.getUploadDriver.bind(uploader),
    createUploader: uploader.createUploader.bind(uploader),
    getOssConfig: uploader.getOssConfig.bind(uploader),
    getCosConfig: uploader.getCosConfig.bind(uploader)
  }
}